package cn.geminis.data.jpa.graphql.utils;

import cn.geminis.data.jpa.GeminiRepository;
import cn.geminis.data.jpa.graphql.fetcher.JpaDataFetcher;
import graphql.Scalars;
import graphql.schema.*;

import javax.persistence.EntityManager;
import javax.persistence.metamodel.EntityType;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

import static graphql.schema.GraphQLArgument.*;
import static graphql.schema.GraphQLFieldDefinition.*;
import static graphql.schema.GraphQLInputObjectField.*;
import static graphql.schema.GraphQLObjectType.*;

/**
 * @author puddi
 */
public class JpaEntityTypeUtils {

    private static final Map<EntityType<?>, GraphQLObjectType> ENTITY_TYPE_CACHE = new HashMap<>();

    private static final GraphQLInputType TYPE_ORDER_BY =
            GraphQLInputObjectType.newInputObject()
                    .name("orderBy")
                    .field(newInputObjectField().name("field").type(Scalars.GraphQLString).build())
                    .field(newInputObjectField().name("order").type(Scalars.GraphQLString).build())
                    .build();
    private static final GraphQLInputType TYPE_FILTER = GraphQLInputObjectType.newInputObject()
            .name("filter")
            .field(newInputObjectField().name("field").type(Scalars.GraphQLString).build())
            .field(newInputObjectField().name("compareType").type(Scalars.GraphQLString).build())
            .field(newInputObjectField().name("value").type(Scalars.GraphQLString).build())
            .build();
    private static final GraphQLInputObjectType TYPE_FILTER_GROUP = GraphQLInputObjectType.newInputObject()
            .name("filterGroup")
            .field(newInputObjectField().name("type").type(Scalars.GraphQLString).build())
            .field(newInputObjectField().name("filters").type(new GraphQLList(TYPE_FILTER)).build())
            .field(newInputObjectField().name("childGroups").type(new GraphQLList(new GraphQLTypeReference("filterGroup"))).build())
            .build();

    public static GraphQLFieldDefinition getEntityField(EntityManager entityManager, GeminiRepository geminiRepository, EntityType<?> entityType) {
        var pageType = newObject()
                .name(entityType.getName() + "Pageable")
                .field(newFieldDefinition().name("totalPages").type(Scalars.GraphQLInt).build())
                .field(newFieldDefinition().name("totalElements").type(Scalars.GraphQLLong).build())
                .field(newFieldDefinition().name("content").type(new GraphQLList(getObjectType(entityType))).build())
                .build();

        return newFieldDefinition()
                .name(entityType.getName())
                .type(pageType)
                .dataFetcher(new JpaDataFetcher(entityManager, geminiRepository, entityType))
                .argument(newArgument().name("page").type(Scalars.GraphQLInt).build())
                .argument(newArgument().name("size").type(Scalars.GraphQLInt).build())
                .argument(newArgument().name("orderBy").type(Scalars.GraphQLString).build())
                .argument(newArgument().name("orderBys").type(new GraphQLList(TYPE_ORDER_BY)).build())
                .argument(newArgument().name("filter").type(TYPE_FILTER_GROUP).build())
//                .arguments(entityType.getAttributes().stream().map(JpaAttributeUtils::getInputField).collect(Collectors.toList()))
                .build();
    }

    private static GraphQLObjectType getObjectType(EntityType<?> entityType) {
        if (ENTITY_TYPE_CACHE.containsKey(entityType)) {
            return ENTITY_TYPE_CACHE.get(entityType);
        }

        var type = newObject()
                .name(entityType.getName())
                .fields(entityType.getAttributes().stream().map(JpaAttributeUtils::getOutputField).collect(Collectors.toList()))
                .build();
        ENTITY_TYPE_CACHE.put(entityType, type);

        return type;
    }


}
